// b15.v
// Verilog version, rewritten by Li Shen, Aug 2002
// Institute of Computing Technology, Chinese Academy of Sciences
// The original VHDL version is b15.vhd from Politecnico di Torino

module b15 (CLOCK, RESET, NA_n, BS16_n, READY_n, HOLD, Datai,
	    W_R_n, D_C_n, M_IO_n, ADS_n, BE_n, Address, Datao);
input CLOCK;
input RESET;
input NA_n;
input BS16_n;
input READY_n;
input HOLD;
input [31:0] Datai; //signed
output W_R_n;
output D_C_n;
output M_IO_n;
output ADS_n;
output [3:0] BE_n;
output [29:0]Address;
output [31:0] Datao; //signed
/////
wire CLOCK;
wire RESET;
wire NA_n;
wire BS16_n;
wire READY_n;
wire HOLD;
wire [31:0] Datai; //signed
reg W_R_n;
reg D_C_n;
reg M_IO_n;
reg ADS_n;
reg [3:0] BE_n;
reg [29:0] Address;
reg [31:0] Datao; //signed
/////
reg StateNA;
reg StateBS16;
reg RequestPending;
reg NonAligned;
reg ReadRequest;
reg MemoryFetch;
reg CodeFetch;
reg [3:0] ByteEnable;
reg [1:0] DataWidth;
reg [2:0] State;
reg [31:0] EAX; //signed
reg [31:0] EBX; //signed
reg [31:0] rEIP; //signed

parameter Pending=1, NotPending=0, WidthByte=0, WidthWord=1, WidthDword=2;

parameter StateInit=0, StateTi=1, StateT1=2,  StateT2=3;
parameter StateT1P=4,  StateTh=5, StateT2P=6, StateT2I=7;

parameter REP=8'hF3, REPNE=8'hF2, LOCK=8'hF0;

parameter CSsop=8'h2E, SSsop=8'h36, DSsop=8'h3E, ESsop=8'h26;
parameter FSsop=8'h64, GSsop=8'h65, OPsop=8'h66, ADsop=8'h67;

parameter MOV_al_b=8'hB0,         MOV_eax_dw=8'hB8;
parameter MOV_ebx_dw=8'hBB,       MOV_ebx_eax=8'h89;
parameter MOV_eax_ebx=8'h8B,      IN_al=8'hE4;
parameter OUT_al=8'hE6,           ADD_al_b=8'h04;
parameter ADD_ax_w=8'h05,         ROL_eax_b=8'hD1;
parameter ROL_al_1=8'hD0,         ROL_al_n =8'hC0;
parameter INC_eax=8'h40,          INC_ebx=8'h43;
parameter JMP_rel_short=8'hEB,    JMP_rel_near=8'hE9;
parameter JMP_intseg_immed=8'hEA, HLT=8'hF4;
parameter WAITx=8'h9B,            NOP=8'h90;
///// P0
reg [31:0] a1; //signed

always @(posedge CLOCK)
    begin
      if (RESET)
	begin
	    BE_n <= 0;
            Address <= 0;
            W_R_n <= 0;
            D_C_n <= 0;
            M_IO_n <= 0;
	    ADS_n <= 0;

	    State <= StateInit;

	    StateNA <= 0;
	    StateBS16 <= 0;
	    DataWidth <= 0;
	end
      else
        case (State)
	    StateInit:
	    begin
		D_C_n <= 1;
		ADS_n <= 1;
		State <= StateTi;
		StateNA <= 1;
		StateBS16 <= 1;
		DataWidth <= 2;
		State <= StateTi;
	    end
            StateTi:
	    begin
                if (RequestPending == Pending)
                    State <= StateT1;
                else if (HOLD)
                    State <= StateTh;
                else
                    State <= StateTi;
	    end
            StateT1:
	    begin
                // Address <= rEIP/4;
                // fs 062299
	      if (rEIP[31]) a1 = -(-rEIP >> 2);
	      else          a1 = rEIP >> 2;
	      Address <= a1 & 32'h3fffffff;
	      BE_n <= ByteEnable;
	      M_IO_n <= MemoryFetch;
	      if (ReadRequest == Pending)
		W_R_n <= 0;
	      else
		W_R_n <= 1;
	      if (CodeFetch == Pending)
		D_C_n <= 0;
	      else
		D_C_n <= 1;
	      ADS_n <= 0;
	      State <= StateT2;
	    end
            StateT2:
	    begin
                if (!READY_n && !HOLD && RequestPending==Pending)
                    State <= StateT1;
//              else if (READY_N && NA_n)
                else if ((RequestPending==Pending||HOLD)&&READY_n&&!NA_n)
                    State <= StateT2I;
                else if (RequestPending==Pending&&!HOLD&&READY_n&&!NA_n)
                    State <= StateT2P;
                else if (RequestPending==NotPending && !HOLD && !READY_n)
                    State <= StateTi;
                else if (HOLD && READY_n)
                    State <= StateTh;
		else
		    State <= StateT2;
		StateBS16 <= BS16_n;
		if (!BS16_n)
		    DataWidth <= WidthWord;
		else
		    DataWidth <= WidthDword;
		StateNA <= NA_n;
		ADS_n <= 1;
	    end
            StateT1P:
	    begin
                if (!NA_n && !HOLD && RequestPending==Pending)
                    State <= StateT2P;
                else if (!NA_n && (HOLD || RequestPending==NotPending))
                    State <= StateT2I;
                else if (NA_n)
                    State <= StateT2;
		else
		    State <= StateT1P;
		StateBS16 <= BS16_n;
		if (!BS16_n)
		    DataWidth <= WidthWord;
		else
		    DataWidth <= WidthDword;
		StateNA <= NA_n;
		ADS_n <= 1;
	    end
            StateTh:
	    begin
                if (!HOLD && RequestPending==Pending)
                    State <= StateT1;
                else if (!HOLD && RequestPending==NotPending)
                    State <= StateTi;
		else
		    State <= StateTh;
	    end
            StateT2P:
	    begin
	        // Address <= rEIP/2;
                // fs 990629
	        if (rEIP[31]) a1 = -(-rEIP >> 1);
	        else          a1 = rEIP >> 1;
	        Address <= a1 & 32'h3fffffff;
                BE_n <= ByteEnable;
                M_IO_n <= MemoryFetch;
                if (ReadRequest==Pending)
                    W_R_n <= 0;
                else
                    W_R_n <= 1;
                if (CodeFetch==Pending)
                    D_C_n <= 0;
                else
                    D_C_n <= 1;
		ADS_n <= 0;
                if (!READY_n)
                    State <= StateT1P;
		else
		    State <= StateT2P;
	    end
            StateT2I:
	    begin
                if (READY_n && RequestPending==Pending && !HOLD)
                    State <= StateT2P;
                else if (!READY_n && HOLD)
                    State <= StateTh;
                else if (!READY_n && !HOLD && RequestPending==Pending)
                    State <= StateT1;
                else if (!READY_n && !HOLD && RequestPending==NotPending)
                    State <= StateTi;
		else
		    State <= StateT2I;
	    end
        endcase
    end
///// P1
reg [7:0] InstQueue[15:0];
reg [4:0] InstQueueRd_Addr;
reg [4:0] InstQueueWr_Addr;
reg [31:0] InstAddrPointer; //signed
reg [31:0] PhyAddrPointer; //signed
reg Extended;
reg More;
reg Flush;
reg [15:0] lWord;
reg [14:0] uWord;
reg [31:0] fWord; //signed
reg [3:0] State2;
reg [31:0] a2; //signed

parameter InstQueueLimit = 15;
parameter Si = 0, S1 = 1, S2 = 2, S3 = 3, S4 = 4;
parameter S5 = 5, S6 = 6, S7 = 7, S8 = 8, S9 = 9;

always @(posedge CLOCK)
    begin
	if (RESET)
	begin
	    State2 = Si;
	    InstQueue[0]=0; InstQueue[1]=0; InstQueue[2]=0; InstQueue[3]=0; 
	    InstQueue[4]=0; InstQueue[5]=0; InstQueue[6]=0; InstQueue[7]=0; 
	    InstQueue[8]=0; InstQueue[9]=0; InstQueue[10]=0; InstQueue[11]=0; 
	    InstQueue[12]=0; InstQueue[13]=0; InstQueue[14]=0; InstQueue[15]=0; 
	    InstQueueRd_Addr = 0;
	    InstQueueWr_Addr = 0;
	    InstAddrPointer = 0;
	    PhyAddrPointer = 0;
	    Extended = 0;
	    More = 0;
	    Flush = 0;
	    lWord = 0;
	    uWord = 0;
	    fWord = 0;
	    CodeFetch <= 0;
            Datao <= 0;
	    EAX <= 0;
	    EBX <= 0;
	    rEIP <= 0;
	    ReadRequest <= 0;
	    MemoryFetch <= 0;
	    RequestPending <= 0;
	end
	else
	case (State2)
	Si:
	begin
            PhyAddrPointer = rEIP;
            InstAddrPointer = PhyAddrPointer;
	    State2 = S1;
	    rEIP <= 20'hFFFF0;
	    ReadRequest <= 1;
	    MemoryFetch <= 1;
	    RequestPending <= 1;
	end
	S1:
	begin
            RequestPending <= Pending;
            ReadRequest <= Pending;
            MemoryFetch <= Pending;
            CodeFetch <= Pending;
	    if (!READY_n)
		State2 = S2;
	    else
		State2 = S1;
	end
	S2:
	begin
            RequestPending <= NotPending;
            InstQueue[InstQueueWr_Addr] = Datai & 255;
            InstQueueWr_Addr = (InstQueueWr_Addr+ 1) & 15;
//            InstQueue(InstQueueWr_Addr) = (Datai / 256) % 256;
            InstQueue[InstQueueWr_Addr] = Datai & 255;
            InstQueueWr_Addr = (InstQueueWr_Addr + 1) & 15;
            if (StateBS16)
	    begin
		if (Datai[31]) a2 = -(-Datai >> 16);
		else           a2 = Datai >> 16;
		InstQueue[InstQueueWr_Addr] = a2 & 255;
		InstQueueWr_Addr = (InstQueueWr_Addr+ 1) & 15;
		if (Datai[31]) a2 = -(-Datai >> 24);
		else           a2 = Datai >> 24;
		InstQueue[InstQueueWr_Addr] = a2 & 255;
		InstQueueWr_Addr = (InstQueueWr_Addr + 1) & 15;
		PhyAddrPointer = PhyAddrPointer + 4;
		State2 = S5;
	    end
            else
	    begin
		PhyAddrPointer = PhyAddrPointer + 2;
		if (PhyAddrPointer[31]) //<0
			rEIP <= -PhyAddrPointer;
		else
			rEIP <= PhyAddrPointer;
		State2 = S3;
	    end
	end
	S3:
	begin
            RequestPending <= Pending;
	    if (!READY_n)
		State2 = S4;
	    else
		State2 = S3;
	end
	S4:
	begin
            RequestPending <= NotPending;
            InstQueue[InstQueueWr_Addr] = Datai & 255;
            InstQueueWr_Addr = (InstQueueWr_Addr + 1) & 15;
            InstQueue[InstQueueWr_Addr] = Datai & 255;
            InstQueueWr_Addr = (InstQueueWr_Addr + 1) & 15;
            PhyAddrPointer = PhyAddrPointer + 2;
	    State2 = S5;
	end
	S5:
	begin
            case (InstQueue[InstQueueRd_Addr])
                NOP:
		begin
                    InstAddrPointer = InstAddrPointer + 1;
                    InstQueueRd_Addr = (InstQueueRd_Addr + 1) & 15;
                    Flush = 0;
                    More = 0;
		end
                OPsop:
		begin
                    InstAddrPointer = InstAddrPointer + 1;
                    InstQueueRd_Addr = (InstQueueRd_Addr + 1) & 15;
                    Extended = 1;
                    Flush = 0;
                    More = 0;
		end
                JMP_rel_short:
		begin
                    if (InstQueueWr_Addr >= InstQueueRd_Addr+3)
		    begin
                        if (InstQueue[(InstQueueRd_Addr+1)&15] > 127)
			begin
                            PhyAddrPointer = InstAddrPointer + 1 - (8'hFF- InstQueue[(InstQueueRd_Addr+1)&15]);
                            InstAddrPointer = PhyAddrPointer;
			end
                        else
			begin
                            PhyAddrPointer = InstAddrPointer + 2 + InstQueue[(InstQueueRd_Addr+1)&15];
                            InstAddrPointer = PhyAddrPointer;
                        end
                        Flush = 1;
                        More = 0;
		    end
                    else
		    begin
                        Flush = 0;
                        More = 1;
                    end
		end
                JMP_rel_near:
		begin
                    if (InstQueueWr_Addr >= InstQueueRd_Addr+5)
		    begin
                        PhyAddrPointer = InstAddrPointer + 5 + InstQueue[(InstQueueRd_Addr +1)&15];
                        InstAddrPointer = PhyAddrPointer;
                        Flush = 1;
                        More = 0;
		    end
                    else
		    begin
                        Flush = 0;
                        More = 1;
                    end
		end
                JMP_intseg_immed:
		begin
                    InstAddrPointer = InstAddrPointer + 1;
                    InstQueueRd_Addr = (InstQueueRd_Addr + 1) & 15;
                    Flush = 0;
                    More = 0;
		end
                MOV_al_b:
		begin
                    InstAddrPointer = InstAddrPointer + 1;
                    InstQueueRd_Addr = (InstQueueRd_Addr+ 1) & 15;
                    Flush = 0;
                    More = 0;
		end
                MOV_eax_dw:
		begin
                    if (InstQueueWr_Addr >= InstQueueRd_Addr+5)
		    begin
                        EAX <= (InstQueue[(InstQueueRd_Addr+4)&15]<<23) + (InstQueue[(InstQueueRd_Addr+3)&15]<<16) + (InstQueue[(InstQueueRd_Addr+2)& 15]<<8) + InstQueue[(InstQueueRd_Addr+1)&15];
                        More = 0;
                        Flush = 0;
                        InstAddrPointer = InstAddrPointer + 5;
                        InstQueueRd_Addr = (InstQueueRd_Addr + 5) & 15;
		    end
                    else
		    begin
                        Flush = 0;
                        More = 1;
                    end
		end
                MOV_ebx_dw:
		begin
                    if (InstQueueWr_Addr >= InstQueueRd_Addr+5)
		    begin
                        EBX <= (InstQueue[(InstQueueRd_Addr+4) & 15]<<23) + (InstQueue[(InstQueueRd_Addr+3)&15]<<16) + (InstQueue[(InstQueueRd_Addr+2)& 15]<<8) + InstQueue[(InstQueueRd_Addr+1)&15]; //corrected!
                        More = 0;
                        Flush = 0;
                        InstAddrPointer = InstAddrPointer + 5;
                        InstQueueRd_Addr = (InstQueueRd_Addr + 5) & 15;
		    end
                    else
		    begin
                        Flush = 0;
                        More = 1;
                    end
		end
                MOV_eax_ebx:
		begin
                    if (InstQueueWr_Addr >= InstQueueRd_Addr+2)
		    begin
			if (EBX[31]) //<0
				rEIP <= -EBX;
			else
	                        rEIP <= EBX;
                        RequestPending <= Pending;
                        ReadRequest <= Pending;
                        MemoryFetch <= Pending;
                        CodeFetch <= NotPending;
			if (!READY_n)
			begin
                          RequestPending <= NotPending;
                          uWord = Datai & 16'h7fff;
                          if (StateBS16)
			  begin
                            // lWord = Datai/(1<<16);
			    // fs 062299
			    lWord = Datai & 16'hffff;
			  end
                          else
			  begin
                            rEIP <= rEIP + 2;
                            RequestPending <= Pending;
			    if (!READY_n)
			    begin
				RequestPending <= NotPending;
				lWord = Datai & 16'hffff;
			    end
                          end
			  if (!READY_n)
			  begin
                            EAX <= (uWord<<16) + lWord;
                            More = 0;
                            Flush = 0;
                            InstAddrPointer = InstAddrPointer + 2;
                            InstQueueRd_Addr = (InstQueueRd_Addr + 2) & 15;
			  end
			end
		    end
                    else
		    begin
                        Flush = 0;
                        More = 1;
                    end
		end
                MOV_ebx_eax:
		begin
                    if (InstQueueWr_Addr >= InstQueueRd_Addr+2)
		    begin
			if (EBX[31]) //<0
	                        rEIP <= -EBX; //corrected!
			else
	                        rEIP <= EBX;
                        lWord = EAX & 16'hffff;
			if (EAX[31]) a2 = -(-EAX >> 16);
			else         a2 = EAX >> 16;
                        uWord = a2 & 16'h7fff;
                        RequestPending <= Pending;
                        ReadRequest <= NotPending;
                        MemoryFetch <= Pending;
                        CodeFetch <= NotPending;
			if (State == StateT1 || State == StateT1P)
			begin
                          Datao <= (uWord<<16) + lWord;
			  if (!READY_n)
			  begin
                            RequestPending <= NotPending;
                            if (!StateBS16)
			    begin
                              rEIP <= rEIP + 2;
                              RequestPending <= Pending;
                              ReadRequest <= NotPending;
                              MemoryFetch <= Pending;
                              CodeFetch <= NotPending;
			      State2 =S6;
                            end
                            More = 0;
                            Flush = 0;
                            InstAddrPointer = InstAddrPointer + 2;
                            InstQueueRd_Addr = (InstQueueRd_Addr + 2) & 15;
			  end
			end
		    end
                    else
		    begin
                        Flush = 0;
                        More = 1;
                    end
		end
                IN_al:
		begin
                    if (InstQueueWr_Addr >= InstQueueRd_Addr+2)
		    begin
                        rEIP <= InstQueueRd_Addr+1;
                        RequestPending <= Pending;
                        ReadRequest <= Pending;
                        MemoryFetch <= NotPending;
                        CodeFetch <= NotPending;
			if (!READY_n)
			begin
                          RequestPending <= NotPending;
                          EAX <= Datai;
                          InstAddrPointer = InstAddrPointer + 2;
                          InstQueueRd_Addr = InstQueueRd_Addr + 2;
                          Flush = 0;
                          More = 0;
			end
		    end
                    else
		    begin
                        Flush = 0;
                        More = 1;
                    end
		end
                OUT_al:
		begin
                    if (InstQueueWr_Addr >= InstQueueRd_Addr+2)
		    begin
                        rEIP <= InstQueueRd_Addr+1;
                        RequestPending <= Pending;
                        ReadRequest <= NotPending;
                        MemoryFetch <= NotPending;
                        CodeFetch <= NotPending;
			if (State == StateT1 || State == StateT1P)
			begin
                            fWord = EAX & 16'hffff;
                            Datao <= fWord;
			    if (!READY_n)
			    begin
                        	RequestPending <= NotPending;
                        	InstAddrPointer = InstAddrPointer + 2;
                        	InstQueueRd_Addr = (InstQueueRd_Addr+2) & 15;
                        	Flush = 0;
                        	More = 0;
			    end
			end
		    end
                    else
		    begin
                        Flush = 0;
                        More = 1;
                    end
		end
                ADD_al_b:
		begin
                    InstAddrPointer = InstAddrPointer + 1;
                    InstQueueRd_Addr = (InstQueueRd_Addr + 1) & 15;
                    Flush = 0;
                    More = 0;
		end
                ADD_ax_w:
		begin
                    InstAddrPointer = InstAddrPointer + 1;
                    InstQueueRd_Addr = (InstQueueRd_Addr + 1) & 15;
                    Flush = 0;
                    More = 0;
		end
                ROL_al_1:
		begin
                    InstAddrPointer = InstAddrPointer + 2;
                    InstQueueRd_Addr = (InstQueueRd_Addr + 2) & 15;
                    Flush = 0;
                    More = 0;
		end
                ROL_al_n:
		begin
                    InstAddrPointer = InstAddrPointer + 2;
                    InstQueueRd_Addr = (InstQueueRd_Addr + 2) & 15;
                    Flush = 0;
                    More = 0;
		end
                INC_eax:
		begin
                    EAX <= EAX + 1;
                    InstAddrPointer = InstAddrPointer + 1;
                    InstQueueRd_Addr = (InstQueueRd_Addr + 1) & 15;
                    Flush = 0;
                    More = 0;
		end
                INC_ebx:
		begin
                    EBX <= EBX + 1;
                    InstAddrPointer = InstAddrPointer + 1;
                    InstQueueRd_Addr = (InstQueueRd_Addr + 1) & 15;
                    Flush = 0;
                    More = 0;
		end
                default:
		begin
                    InstAddrPointer = InstAddrPointer + 1;
                    InstQueueRd_Addr = (InstQueueRd_Addr + 1) & 15;
                    Flush = 0;
                    More = 0;
		end
            endcase
	    if (!(InstQueueRd_Addr < InstQueueWr_Addr) || (InstQueueLimit < InstQueueRd_Addr+4) || Flush || More)
		State2 = S7;
	end
	S6:
	begin
		Datao <= (uWord<<16) + lWord;
		if (!READY_n)
		begin
		    RequestPending <= NotPending;
		    State2 = S5;
		end
	end
	S7:
	begin
          if (Flush)
	  begin
            InstQueueRd_Addr = 1;
            InstQueueWr_Addr = 1;

	    if (InstAddrPointer[31]) //<0
		fWord = -InstAddrPointer;
	    else
	     	fWord = InstAddrPointer;

            if (fWord & 1)
                InstQueueRd_Addr = (InstQueueRd_Addr + (fWord & 3)) & 15;
          end
          if (InstQueueLimit < InstQueueRd_Addr+3)
	  begin
	    State2 = S8;
            InstQueueWr_Addr = 0;
	  end
	  else
	    State2 = S9;
	end
	S8:
	begin
	    if (InstQueueRd_Addr <= InstQueueLimit)
	    begin
                InstQueue[InstQueueWr_Addr] = InstQueue[InstQueueRd_Addr];
                InstQueueRd_Addr = (InstQueueRd_Addr + 1) & 15;
                InstQueueWr_Addr = (InstQueueWr_Addr + 1) & 15;
		State2 = S8;
	    end
	    else
	    begin
                InstQueueRd_Addr = 0;
		State2 = S9;
	    end
	end
	S9:
	begin
            rEIP <= PhyAddrPointer;
	    State2 = S1;
	end
	endcase
    end
//// P2
always @(posedge CLOCK)
    begin
	if (RESET)
	begin
	    ByteEnable <= 0;
	    NonAligned <= 0;
	end
	else
        case (DataWidth)
            WidthByte:
                case (rEIP & 3)
                    0: ByteEnable <= 4'b1110;
                    1: ByteEnable <= 4'b1101;
                    2: ByteEnable <= 4'b1011;
                    3: ByteEnable <= 4'b0111;
                endcase
            WidthWord:
                case (rEIP & 3)
                    0: begin
                         ByteEnable <= 4'b1100;
                         NonAligned <= NotPending;
		       end
                    1: begin
                         ByteEnable <= 4'b1001;
                         NonAligned <= NotPending;
		       end
                    2: begin
                         ByteEnable <= 4'b0011;
                         NonAligned <= NotPending;
		       end
                    3: begin
                         ByteEnable <= 4'b0111;
                         NonAligned <= Pending;
		       end
                endcase
            WidthDword:
                case (rEIP & 3)
                    0: begin
                         ByteEnable <= 4'b0000;
                         NonAligned <= NotPending;
		       end
                    1: begin
                         ByteEnable <= 4'b0001;
                         NonAligned <= Pending;
		       end
                    2: begin
                         NonAligned <= Pending;
                         ByteEnable <= 4'b0011;
		       end
                    3: begin
                         NonAligned <= Pending;
                         ByteEnable <= 4'b0111;
		       end
                endcase
        endcase
    end
endmodule
